/*/////////////////////////////////////////////////////////////////////////////////
/// An
///    ___   ____ ___ _____ ___  ____
///   / _ \ / ___|_ _|_   _/ _ \|  _ \
///  | | | | |  _ | |  | || | | | |_) |
///  | |_| | |_| || |  | || |_| |  _ <
///   \___/ \____|___| |_| \___/|_| \_\
///                              File
///
/// Copyright (c) 2008-2009 Ismail TARIM <ismail@royalspor.com> and the Ogitor Team
//
//This program is free software; you can redistribute it and/or modify it under
//the terms of the GNU Lesser General Public License as published by the Free Software
//Foundation; either version 2 of the License, or (at your option) any later
//version.
//
//This program is distributed in the hope that it will be useful, but WITHOUT
//ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
//FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
//
//You should have received a copy of the GNU Lesser General Public License along with
//this program; if not, write to the Free Software Foundation, Inc., 59 Temple
//Place - Suite 330, Boston, MA 02111-1307, USA, or go to
//http://www.gnu.org/copyleft/lesser.txt.
////////////////////////////////////////////////////////////////////////////////*/

#pragma once

#include "OgitorsSingleton.h"

namespace Ogitors
{
    //! Undo manager class
    /*!  
        A class that handles undo/redo operations
    */
    class OgitorExport OgitorsUndoManager: public Singleton<OgitorsUndoManager>
    {
    public:
        /**
        * Constructor
        */
        OgitorsUndoManager();
        /**
        * Destructor
        */
        virtual ~OgitorsUndoManager();
        /**
        * Adds an undo operation to the queue
        * @param undodata undo operation information
        */
        void AddUndo(OGITORUNDOSTRUCT undodata);
        /**
        * Reverts last operation
        */
        void Undo();
        /**
        * Applies the undo operation
        * @param undostruct undo operation information
        * @param setselected flag signifying if object of operation is to be selected
        * @todo should be protected/private function
        */
        void ApplyUndo(OGITORUNDOSTRUCT &undostruct, bool setselected);
        /**
        * Re-applies next change in the queue
        */
        void Redo();
        /**
        * Applies the redo operation
        * @param undostruct redo operation information
        * @param setselected flag signifying if object of operation is to be selected
        * @todo should be protected/private function
        */
        void ApplyRedo(OGITORUNDOSTRUCT &undostruct, bool setselected);
        /**
        * String'ifies last undo structure information
        * @return string'ified last undo structure information
        */
        inline Ogre::String GetUndoString()
        {
            Ogre::String str = "Undo: ";
            if(!CanUndo())
                str += "<None>";
            else
            {
                str += m_List[m_CurrentIndex].m_ObjectProperties["name"];
                if(m_List[m_CurrentIndex].m_Operation == UNDO_CREATE)
                    str += " CREATE";
                else if(m_List[m_CurrentIndex].m_Operation == UNDO_DELETE)
                    str += " DELETE";
                else 
                    str += " Property Change";
            }
            return str;
        };
        /**
        * String'ifies last redo structure information
        * @return string'ified last redo structure information
        */
        inline Ogre::String GetRedoString()
        {
            Ogre::String str = "Redo: ";
            if(!CanRedo())
                str += "<None>";
            else
            {
                str += m_List[m_CurrentIndex + 1].m_ObjectProperties["name"];
                if(m_List[m_CurrentIndex + 1].m_Operation == UNDO_CREATE)
                    str += " CREATE";
                else if(m_List[m_CurrentIndex + 1].m_Operation == UNDO_DELETE)
                    str += " DELETE";
                else 
                    str += " Property Change";
            }
            return str;
        };
        /**
        * Tests if an undo operation can be performed
        */
        inline bool CanUndo() 
        {
            return m_CurrentIndex > -1;
        };
        /**
        * Tests if a redo operation can be performed
        */
        inline bool CanRedo()
        {
            return (m_CurrentIndex < ((int)m_List.size() - 1));
        };
        /**
        * Tests if paste operation can be performed
        */
        inline bool CanPaste()
        {
            return (m_Clipboard.size() > 0);
        };
        /**
        * Copies selected object to the internal clipboard
        * @param object the object to be copied
        */
        void Copy(CBaseEditor *object);
        /**
        * Copies given undodata to the internal clipboard
        * @param pointer to undodata to be copied
        */
        void Copy(OGITORUNDOSTRUCT *copydata);
        /**
        * Returns a list of names of objects int the clipboard
        * @param list the vector of object names
        */
        void GetPasteNames(Ogre::StringVector& list);
        /**
        * Pastes the object stored in the internal clipboard 
        * @param object parent object to the one being pasted
		* @param clipboardindex the index of the object to paste in the clipboard
        */
        void Paste(CBaseEditor *parent, int clipboardindex = -1);
        /**
        * Copies selected object to a template file
        * @param object the object to be copied
        * @param templateName the template name to be used (also defines the template file name and must be unique)
        * @see VerifyTemplateName for verifying template names
        * @param projectscope if set to true, it adds the template as project scoped
        * @return returns a pointer to the new template struct
        */
        const OGITORTEMPLATESTRUCT *CopyToTemplate(CBaseEditor *object, const Ogre::String& templateName, bool projectscope);
        /**
        * Copies selected object and all of its children to a template file
        * @param object the object to be copied
        * @param templateName the template name to be used (also defines the template file name and must be unique)
        * @see VerifyTemplateName for verifying template names
        * @param projectscope if set to true, it adds the template as project scoped
        * @return returns a pointer to the new template struct
        */
        const OGITORTEMPLATESTRUCT *CopyToTemplateWithChildren(CBaseEditor *object, const Ogre::String& templateName, bool projectscope);
        /**
        * Verifies if a given template name is unique
        * @param name the name to be verified
        * @return returns true if the name is unique
        */
        bool VerifyTemplateName(const Ogre::String& name);
        /**
        * Reads a template file from the disk
        * @param filename the name of the file to be read
        * @param templates a TemplateList to be filled with the templates read from the file
        * @see TemplateList for details
        * @return Returns false if there is a problem loading the file
        */
        bool ReadTemplateFile(const Ogre::String& filename, TemplateList& templates);
        /**
        * Reads a list of template files from the disk
        * @param filenames the vector of file names to be read
        */
        void ReadTemplateFiles(const Ogre::StringVector& filenames);
        /**
        * Returns a list of template names
        * @param list the vector of template names
        * @param scope scope = 0 return general, 1 = return project scope, 2 = return all 
        */
        void GetTemplateNames(Ogre::StringVector& list, int scope);
        /**
        * Returns a pointer to requested template's mData
        * @param name the name of the template
        * @return mData of requested template 
        */
        OGITORUNDOSTRUCT *GetTemplateData(const Ogre::String& name);
        /**
        * Creates the object from the given template
        * @param parent the parent object to create the object in
        * @param data the template data
        * @param position the position the object will be instantiated at 
        * @param addtotree If set to true, object will be added to scene view tree
        * @return Returns the created object 
        */
		CBaseEditor *InstantiateTemplate(CBaseEditor *parent, OGITORUNDOSTRUCT *data, Ogre::Vector3 position, bool addtotree);
        /**
        * Sets the Current Clipboard Buffer's Max Size to desired value
        * @param newsize the new max size of Clipboard Buffer
        */
		static void setClipboardMaxSize(unsigned int newsize)
		{
			if(newsize < 1)
				newsize = 1;
			m_ClipboardMaxSize = newsize;
		}
        /**
        * Returns the current Clipboard Buffer's Max Size
		* @return max size of clipboard buffer
        */
		static unsigned int getClipboardMaxSize()
		{
			return m_ClipboardMaxSize;
		}


    protected:
        UndoList            m_List;                   /** Undo list that stores undo/redo structures*/
        int                 m_CurrentIndex;           /** Current operation index */
        UndoList            m_Clipboard;              /** Clipboard copy/paste list */
        TemplateList        m_Templates;              /** Templates list */
		
		static unsigned int m_ClipboardMaxSize;          /** The number of items the clipboard will store */

        /**
        * Clears specified list between specified locations
        * @param list list to be cleared
        * @param itbegin iterator handle of start location
        * @param itend iterator handle of end location
        */
        void ClearList(UndoList &list, UndoList::iterator itbegin, UndoList::iterator itend);
        /**
        * Clears Templates List
        * @param projectscope if set to true, it only clears the project scoped templates
        */
        void ClearTemplates(bool projectscope);
        /**
        * Clears specified list item(s)
        * @param item item(s) list to be cleared
        */
        void ClearListItem(UndoList &item);
        /**
        * Clears custom property list item(s)
        * @param customlist list to be cleared
        */
        void ClearCustomPropertyData(CustomPropertyList &customlist);
        /**
        * Pastes selected object into specified undo/redo frame
        * @param parent parent object to the selected one
        * @param undostruct undo/redo information frame to paste object into
        * @param setselected flag signifying if pasted object is to be selected or not after operation is complete
        * @param addtotree flag signifying if pasted object is to be added to scene view tree
        * @return returns a pointet to the pasted object
        */
        CBaseEditor *Paste(CBaseEditor *parent, OGITORUNDOSTRUCT &undostruct, bool setselected, bool addtotree);
        /**
        * Writes a template file to the disk
        * @param templatestruct Reference to the OGITORTEMPLATESTRUCT to be written
        * @see OGITORTEMPLATESTRUCT for details
        */
        void WriteTemplate(OGITORTEMPLATESTRUCT& templatestruct);
        /**
        * Recursively writes given object and all of its children to stream
        * @param stream the stream to write to
        * @param object the object to be written
        * @param writechildren write the children of the object if set to true
        * @param indent number of starting spaces
        */
        void WriteTemplateObject(std::ostream& stream, CBaseEditor *object, bool writechildren, int indent);
        /**
        * Recursively reads the objects from a given xml element
        * @param rootelement the element to read from
        * @param data the struct to write object to
        */
        void ReadTemplateObject(TiXmlElement *rootelement, OGITORUNDOSTRUCT& data);
    };
};
